package nachos.threads;
import java.util.HashMap;
import java.util.Iterator;

import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


import nachos.machine.*;



/**
 * Uses the hardware timer to provide preemption, and to allow threads to sleep
 * until a certain time.
 */
public class Alarm {
    /**
     * Allocate a new Alarm. Set the machine's timer interrupt handler to this
     * alarm's callback.
     *
     * <p><b>Note</b>: Nachos will not function correctly with more than one
     * alarm.
     */
    public Alarm() {
	Machine.timer().setInterruptHandler(new Runnable() {
		public void run() { timerInterrupt(); }
	    });
    }

    /**
     * The timer interrupt handler. This is called by the machine's timer
     * periodically (approximately every 500 clock ticks). Causes the current
     * thread to yield, forcing a context switch if there is another thread
     * that should be run.
     */
    public void timerInterrupt() {
		KThread.currentThread().yield();
		//System.out.println("TIMERINTERRUPT at " + Machine.timer().getTime());
		
		Set <Map.Entry<KThread,Long>> threadset = aqueue.entrySet();  
		Iterator<Entry<KThread, Long>> it1 = threadset.iterator();
		while(it1.hasNext())
		{
			Map.Entry<KThread, Long> entry = it1.next();
			if(Machine.timer().getTime()>entry.getValue())
			{
				entry.getKey().ready();
				it1.remove();
			}
		}
    }

    /**
     * Put the current thread to sleep for at least <i>x</i> ticks,
     * waking it up in the timer interrupt handler. The thread must be
     * woken up (placed in the scheduler ready set) during the first timer
     * interrupt where
     *
     * <p><blockquote>
     * (current time) >= (WaitUntil called time)+(x)
     * </blockquote>
     *
     * @param	x	the minimum number of clock ticks to wait.
     *
     * @see	nachos.machine.Timer#getTime()
     */
    //put to sleep and awake in timerInterrupt()
    public void waitUntil(long x) {
		
		long wakeTime = Machine.timer().getTime() + x;
		aqueue.put(KThread.currentThread(),wakeTime);
		boolean intStatus = Machine.interrupt().disable();
		KThread.currentThread().sleep();
		Machine.interrupt().restore(intStatus);
		
	    }
	    
    HashMap< KThread,Long > aqueue = new HashMap<KThread,Long>();    
}
